package com.google.code.stalkernet.chat.ui.action;

import java.awt.event.ActionEvent;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.Executor;

import javax.swing.AbstractAction;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

import com.google.code.stalkernet.database.Database;
import com.google.code.stalkernet.entry.EntryBody;
import com.google.code.stalkernet.entry.EntryUtils;

/**
 * Action that send message.
 *
 * @author Mikhail Vladimirov
 */
public class SendMessageAction
    extends AbstractAction
    implements DocumentListener
{
    private final Document document;
    private final Database database;
    private final Executor executor;
    private final String author;

    /**
     * Create new send message action that takes message top be sent from given
     * {@link Document}.
     *
     * @param document {@link Document} to take message from
     * @param database {@link Database} to add message to
     * @param executor {@link Executor} to use for database-related operations
     * @param author author for new added messages
     */
    public SendMessageAction (
        Document document, Database database, Executor executor, String author)
    {
        super ("Send Message");

        if (document == null)
            throw new IllegalArgumentException ("Document is null");

        if (database == null)
            throw new IllegalArgumentException ("Database is null");

        if (executor == null)
            throw new IllegalArgumentException ("Executor is null");

        if (author == null)
            throw new IllegalArgumentException ("Author is null");

        this.document = document;
        this.database = database;
        this.executor = executor;
        this.author = author;

        document.addDocumentListener (this);

        update ();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void actionPerformed (ActionEvent e)
    {
        int length = document.getLength ();
        if (length > 0)
        {
            ByteArrayOutputStream output = new ByteArrayOutputStream ();
            DataOutput dataOutput = new DataOutputStream (output);
            try
            {
                dataOutput.writeLong (System.currentTimeMillis ());
                dataOutput.writeUTF (author);
                dataOutput.writeUTF (
                    document.getText (0, document.getLength ()));
                document.remove (0, length);
            }
            catch (IOException ex)
            {
                throw new Error ("Impossible", ex);
            }
            catch (BadLocationException ex)
            {
                throw new Error ("Impossible", ex);
            }

            executor.execute (
                new AddEntryRunnable (
                    EntryUtils.createEntryBody (output.toByteArray ())));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void insertUpdate (DocumentEvent e)
    {
        update ();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeUpdate (DocumentEvent e)
    {
        update ();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void changedUpdate (DocumentEvent e)
    {
        update ();
    }

    private void update ()
    {
        setEnabled (document.getLength () > 0);
    }

    private class AddEntryRunnable implements Runnable
    {
        private final EntryBody entryBody;

        public AddEntryRunnable (EntryBody entryBody)
        {
            if (entryBody == null)
                throw new IllegalArgumentException ("Entry body is null");

            this.entryBody = entryBody;
        }

        @Override
        public void run ()
        {
            database.addEntry (database.createEntry (entryBody));
        }
    }
}
